use super::bsm::Option;
use crate::stochastics::gbm::{GeoBrown, Stochastic};
extern crate nalgebra as na;
use crate::tools::tool;
use na::*;
use rand::thread_rng;
use rand_distr::{Distribution, Normal, NormalError};
use rayon::prelude::*;
use std::cmp;
extern crate docify;
fn init_standart_arr(n: usize) -> Vec<f64> {
    let mut res = Vec::new();
    for i in 0..n {
        let v = tool::random_from_stdnorm();
        res.push(v);
    }
    res
}

// DMatrix::from_vec(steps, sample_size, data)
#[doc = docify::embed!("src/option_price/mont_carlo.rs", test_mont)]
impl Option {
    fn geo_brown(&self, sigma: f64, steps: usize) -> GeoBrown {
        let geo = GeoBrown {
            S0: self.S,
            T: self.T,
            steps: steps,
            sigma: sigma,
            mu: self.r - self.divdend - sigma.powf(2.0) / 2.0,
        };
        geo
    }
    pub fn mont_price(&self, sigma: f64, steps: usize, sample_size: usize) -> f64 {
        let geo = self.geo_brown(sigma, steps);
        let price: Vec<f64> = (0..sample_size)
            .into_par_iter()
            .map(|i| -> f64 {
                let simu = geo.gen_path();
                let vv = if self.is_call {
                    simu[steps - 1] - self.K
                } else {
                    self.K - simu[steps - 1]
                };
                let v = if vv > 0.0 { vv } else { 0.0 };
                v
            })
            .collect();

        let mean = price.iter().sum::<f64>() / price.len() as f64;
        mean
    }

    pub fn asia_price(&self, sigma: f64, steps: usize, sample_size: usize) -> f64 {
        let geo = self.geo_brown(sigma, steps);
        let price: Vec<f64> = (0..sample_size)
            .into_par_iter()
            .map(|i| -> f64 {
                let simu = geo.gen_path();
                let av = simu.iter().sum::<f64>() / simu.len() as f64;

                let vv = if self.is_call {
                    av - self.K
                } else {
                    self.K - av
                };

                let v = if vv > 0.0 { vv } else { 0.0 };
                v
            })
            .collect();

        let mean = price.iter().sum::<f64>() / price.len() as f64;
        mean
    }

    pub fn lookback_price(&self, sigma: f64, steps: usize, sample_size: usize) -> f64 {
        let geo = self.geo_brown(sigma, steps);
        let price: Vec<f64> = (0..sample_size)
            .into_par_iter()
            .map(|i| -> f64 {
                let simu = geo.gen_path();
                let mx = simu.iter().max_by(|a, b| a.total_cmp(b)).unwrap();

                let vv = if self.is_call {
                    mx - self.K
                } else {
                    self.K - mx
                };
                let v = if vv > 0.0 { vv } else { 0.0 };
                v
            })
            .collect();
        let mean = price.iter().sum::<f64>() / price.len() as f64;
        mean
    }
}
#[test]
#[docify::export]
fn test_mont() {
    let op = Option {
        is_call: true,
        S: 217.36, // base asset price
        K: 220.0,  // exacuate price
        T: 13.0 / 365.0,
        divdend: 0.44 / 100.0, //divdent rate
        r: 4.62 / 100.0,       //risk free rate
    };

    let vol = 35.23 / 100.0;
    let a = op.mont_price(vol, 14, 100);
    // dbg!(a);
}
